#include "Image.h"
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;
Image::Image(int h, int w) :Matrix(h, w) {}
Image::Image(int h, int w, unsigned char val) : Matrix(h, w, val) {}
Image::Image(const char* ImageName)
{
    this->ReadBMP(ImageName);
}
Image::Image(unsigned char m[][100], int rows)
{
    Mat_he = rows;
    Mat_w = 100;
    Mat_da = new double* [Mat_he];

    for (int i = 0; i < Mat_he; i++)
    {
        Mat_da[i] = new double[Mat_w];
        for (int j = 0; j < Mat_w; j++)
        {
            Mat_da[i][j] = static_cast<unsigned char>(m[i][j]);
        }
    }
}
Image::Image(unsigned char** m, int h, int w)

{
    Mat_he = h;
    Mat_w = w;
    Mat_da = new double* [Mat_he];

    for (int i = 0; i < Mat_he; i++)
    {
        Mat_da[i] = new double[Mat_w];
        for (int j = 0; j < Mat_w; j++)
        {
            Mat_da[i][j] = static_cast<unsigned char>(m[i][j]);
        }
    }
}
Image::Image(const Matrix& m) :Matrix(m) {}
Image::Image(const Image& mat) : Matrix(mat)
{
    bmpfileheader = mat.bmpfileheader;
    bitmapinfoheader = mat.bitmapinfoheader;
}
Image::~Image() {}
void Image::ReadBMP(const char* filename)
{
    FILE* fp;
    fopen_s(&fp, filename, "rb");
    if (!fp)
    {
        cout << "cannot open file";
        exit(0);

    }
    fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);

    Mat_he = bitmapinfoheader.biHeight;
    Mat_w = bitmapinfoheader.biWidth;

    Mat_da = new double* [Mat_he];
    for (int i = 0; i < Mat_he; i++)
    {
        Mat_da[i] = new double[Mat_w];
        for (int j = 0; j < Mat_w; j++)
        {
            unsigned char tdb[3] = { 0 };
            fread(&tdb[0], 1, 1, fp);
            fread(&tdb[1], 1, 1, fp);
            fread(&tdb[2], 1, 1, fp);
            Mat_da[i][j] = (static_cast<double>(tdb[0] + tdb[1] + tdb[2])) / 3;
        }
    }

    fclose(fp);
}
void Image::WriteBMP(const char* filename)
{
    FILE* fp = NULL;
    fopen_s(&fp, filename, "wb");
    bool flag = false;
    while (Mat_w % 4)
    {
        Mat_w++;
    }

    bitmapinfoheader.biWidth = Mat_w;
    bitmapinfoheader.biHeight = Mat_he;
    bitmapinfoheader.biSizeImage = flag ?
        ((((Mat_w * bitmapinfoheader.biBitCount) + 31) / 32 * 4) * Mat_he) : Mat_w * Mat_he;

    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);

    unsigned char t = 0;
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        for (int j = 0; j < bitmapinfoheader.biWidth; j++)
        {
            t = static_cast<unsigned char>(Mat_da[i][j]);
            fwrite(&t, 1, 1, fp);
            fwrite(&t, 1, 1, fp);
            fwrite(&t, 1, 1, fp);
        }
    }

    fclose(fp);
}
void Image::Flip(int code)
{
    double** t_data = new double* [Mat_he];
    for (int i = 0; i < Mat_he; i++)
    {
        t_data[i] = new double[Mat_w];
        for (int j = 0; j < Mat_w; j++)
        {
            t_data[i][j] = Mat_da[i][j];
        }
    }

    if (code)
    {
        for (int i = 0; i < Mat_he / 2; i++)
        {
            double* temp = t_data[i];
            t_data[i] = t_data[Mat_he - 1 - i];
            t_data[Mat_he - 1 - i] = temp;
        }
        myImageFree(Mat_da, Mat_he);
        Mat_da = t_data;
    }
    else
    {
        for (int i = 0; i < Mat_he; i++)
        {
            for (int j = 0; j < Mat_w / 2; j++)
            {
                double temp = t_data[i][j];
                t_data[i][j] = t_data[i][Mat_w - 1 - j];
                t_data[i][Mat_w - 1 - j] = temp;
            }
        }
        myImageFree(Mat_da, Mat_he);
        Mat_da = t_data;
    }
}

void Image::Resize(int h, int w)
{

    int oldw = Mat_w;
    int oldh = Mat_he;
    int neww = w;
    int newh = h;



    if (ceil(neww * 1.0 / oldw) <= 2)
    {
        if (ceil(newh * 1.0 / oldh) <= 2)
        {
            Resize(newh, neww, 1);
        }
        else
        {
            int cnt = newh / oldh;
            int temph = oldh;
            for (int i = 0; i < cnt - 1; i++)
            {
                temph += oldh;
                Resize(temph, neww, 1);
            }
            Resize(temph + (newh % oldh), neww, 1);
        }
    }
    else
    {
        if (ceil(newh * 1.0 / oldh) <= 2)
        {
            int cnt = neww / oldw;
            int tempw = oldw;
            for (int i = 0; i < cnt - 1; i++)
            {
                tempw += oldw;
                Resize(newh, tempw, 1);
            }
            Resize(newh, tempw + (neww % oldw), 1);
        }
        else
        {
            int cnt = newh / oldh;
            int temph = oldh;
            for (int i = 0; i < cnt - 1; i++)
            {
                temph += oldh;
                Resize(temph, oldw, 1);
            }
            Resize(temph + (newh % oldh), oldw, 1);

            cnt = neww / oldw;
            int tempw = oldw;
            for (int i = 0; i < cnt - 1; i++)
            {
                tempw += oldw;
                Resize(newh, tempw, 1);
            }
            Resize(newh, tempw + (neww % oldw), 1);
        }
    }
}
void Image::Resize(int h, int w, int)
{
    int oldh = h;
    int oldw = w;
    int width = Mat_w;
    int height = Mat_he;
    int dw = static_cast<int>(fabs(width - w));
    int dh = static_cast<int>(fabs(height - h));
    if (dw < 1)
    {
        dw = 1;
    }
    if (dh < 1)
    {
        dh = 1;
    }
    int maxw = w > width ? w : width;
    int maxh = h > height ? h : height;
    int difw = maxw / dw;
    int difh = maxh / dh;
    double** data = new double* [h];
    for (int i = 0, prii = 0; i < h; i++)
    {
        data[i] = new double[w];
        if ((i + 1) % difh == 0)
        {
            if (h < height)
            {
                prii++;
            }
            else
            {
                continue;
            }
        }
        for (int j = 0, prij = 0; j < w; j++)
        {
            if ((j + 1) % difw == 0)
            {
                if (w < width)
                {
                    prij++;
                }
                else
                {
                    continue;
                }
            }
            data[i][j] = Mat_da[prii][prij];
            if (prij < width - 1)
                prij++;
        }
        if (prii < height - 1)
            prii++;
    }
    if (w > width)
    {
        for (int i = 0; i < h; i++)
        {
            for (int j = 0; j < w; j++)
            {
                if ((j + 1) % difw == 0)
                {
                    data[i][j] = data[i][j - 1];
                }
            }
        }
    }

    if (h > height)
    {
        for (int i = 0; i < h; i++)
        {
            if ((i + 1) % difh == 0)
            {
                for (int j = 0; j < w; j++)
                {
                    data[i][j] = data[i - 1][j];
                }
            }
        }
    }
    myImageFree(Mat_da, Mat_he);
    Mat_da = data;
    Mat_he = h;
    Mat_w = w;
}

void Image::Cut(int x1, int y1, int x2, int y2)
{
    if (x2 > x1 && y2 > y1)
    {
        int h = y2 - y1 + 1;
        int w = x2 - x1 + 1;
        while (w % 4)
        {
            w++;
        }
        double** t_data = new double* [h];
        for (int i = y1, m = 0; i <= y2; i++, m++)
        {
            t_data[m] = new double[w];
            for (int j = x1, n = 0; j <= x2; j++, n++)
            {
                t_data[m][n] = Mat_da[i][j];
            }
        }

        myImageFree(Mat_da, h);
        Mat_da = t_data;
        Mat_he = h;
        Mat_w = w;
    }
    else
    {
        cout << "error" << endl;
    }
}
void Image::Rotate(int degree)
{
    if (degree == 90)
    {
        int temp = Mat_w;
        Mat_w = Mat_he;
        Mat_he = temp;
        double** data = new double* [Mat_he];
        for (int i = 0; i < Mat_he; i++)
        {
            data[i] = new double[Mat_w];
            for (int j = 0; j < Mat_w; j++)
            {
                data[i][j] = Mat_da[Mat_w - j - 1][i];
            }
        }

        myImageFree(Mat_da, Mat_he);
        Mat_da = data;
    }
    else if (degree == 180)
    {
        double** data = new double* [Mat_he];
        for (int i = 0; i < Mat_he; i++)
        {
            data[i] = new double[Mat_w];
            for (int j = 0; j < Mat_w; j++)
            {
                data[i][j] = Mat_da[Mat_he - i - 1][Mat_w - j - 1];
            }
        }
        myImageFree(Mat_da, Mat_he);
        Mat_da = data;
    }
    else if (degree == 270)
    {
        int temp = Mat_w;
        Mat_w = Mat_he;
        Mat_he = temp;
        double** data = new double* [Mat_he];
        for (int i = 0; i < Mat_he; i++)
        {
            data[i] = new double[Mat_w];
            for (int j = 0; j < Mat_w; j++)
            {
                data[i][j] = Mat_da[j][Mat_he - 1 - i];
            }
        }
        myImageFree(Mat_da, Mat_he);
        Mat_da = data;
    }
    else
    {
        cout << "error" << endl;
    }
}


double Image::Mean()
{
    double sum = 0;
    for (int i = 0; i < Mat_he; i++)
    {
        for (int j = 0; j < Mat_w; j++)
        {
            sum += Mat_da[i][j];
        }
    }
    return sum / (Mat_he * Mat_w);
}
double Image::Variance()
{
    double sum = 0;
    double ave = Mean();
    for (int i = 0; i < Mat_he; i++)
    {
        for (int j = 0; j < Mat_w; j++)
        {
            sum += (Mat_da[i][j] - ave) * (Mat_da[i][j] - ave);
        }
    }
    return sum / (Mat_he * Mat_w);
}


void Image::myImageFree(double** data, int h)
{
    if (data != NULL)
    {
        for (int i = 0; i < h; i++)
        {
            if (data[i] != NULL)
            {
                delete[] data[i];
                data[i] = NULL;
            }
        }
        delete[] data;
        data = NULL;
    }
}
void Image::showImage()
{
    cout << "height = " << Mat_he << endl;
    cout << "width = " << Mat_w << endl;
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            cout << Mat_da[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;

}


void Image::old(const char* ImageName)
{
    FILE* fp = NULL;
    fopen_s(&fp, ImageName, "rb");
    if (!fp)
    {
        cout << "cannot open file";
        exit(0);
    }
    fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);
    /*data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);*/
    /*data = new unsigned char* [bitmapinfoheader.biHeight];
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        data[i] = new unsigned char[bitmapinfoheader.biWidth];
    }*/
    unsigned char r, g, b;
    int width = bitmapinfoheader.biWidth;
    int height = bitmapinfoheader.biHeight;
    FILE* fw = NULL;
    fopen_s(&fw, "Copy.bmp", "wb");
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fw);
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fw);
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            fread(&r, sizeof(unsigned char), 1, fp);
            fread(&g, sizeof(unsigned char), 1, fp);
            fread(&b, sizeof(unsigned char), 1, fp);
            r = (r * 393 + g * 769 + b * 189) / 1000;
            g = (r * 349 + g * 686 + b * 168) / 1000;
            b = (r * 272 + g * 534 + b * 131) / 1000;

            r = r > 255 ? 255 : r;
            b = b > 255 ? 255 : b;
            g = g > 255 ? 255 : g;

            fwrite(&r, sizeof(unsigned char), 1, fw);
            fwrite(&g, sizeof(unsigned char), 1, fw);
            fwrite(&b, sizeof(unsigned char), 1, fw);
        }
    }
    fclose(fw);
    fclose(fp);
}
void Image::Warm(const char* ImageName)
{
    FILE* fp = NULL;
    fopen_s(&fp, ImageName, "rb");
    if (!fp)
    {
        cout << "cannot open file";
        exit(0);
    }
    fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);
    /*data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);*/
    /*data = new unsigned char* [bitmapinfoheader.biHeight];
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        data[i] = new unsigned char[bitmapinfoheader.biWidth];
    }*/
    unsigned char r, g, b;
    int width = bitmapinfoheader.biWidth;
    int height = bitmapinfoheader.biHeight;
    FILE* fw = NULL;
    fopen_s(&fw, "Copy.bmp", "wb");
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fw);
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fw);
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            fread(&r, sizeof(unsigned char), 1, fp);
            fread(&g, sizeof(unsigned char), 1, fp);
            fread(&b, sizeof(unsigned char), 1, fp);
            g = g + 30;
            b = b + 30;
            r = r > 255 ? 255 : r;
            b = b > 255 ? 255 : b;
            g = g > 255 ? 255 : g;

            fwrite(&r, sizeof(unsigned char), 1, fw);
            fwrite(&g, sizeof(unsigned char), 1, fw);
            fwrite(&b, sizeof(unsigned char), 1, fw);
        }
    }
    fclose(fw);
    fclose(fp);
}
void Image::cold(const char* ImageName)
{
    FILE* fp = NULL;
    fopen_s(&fp, ImageName, "rb");
    if (!fp)
    {
        cout << "cannot open file";
        exit(0);
    }
    fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);
    /*data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);*/
    /*data = new unsigned char* [bitmapinfoheader.biHeight];
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        data[i] = new unsigned char[bitmapinfoheader.biWidth];
    }*/
    unsigned char r, g, b;
    int width = bitmapinfoheader.biWidth;
    int height = bitmapinfoheader.biHeight;
    FILE* fw = NULL;
    fopen_s(&fw, "Copy.bmp", "wb");
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fw);
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fw);
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            fread(&r, sizeof(unsigned char), 1, fp);
            fread(&g, sizeof(unsigned char), 1, fp);
            fread(&b, sizeof(unsigned char), 1, fp);

            r = r + 30;
            g = g + 30;
            r = r > 255 ? 255 : r;
            b = b > 255 ? 255 : b;
            g = g > 255 ? 255 : g;

            fwrite(&r, sizeof(unsigned char), 1, fw);
            fwrite(&g, sizeof(unsigned char), 1, fw);
            fwrite(&b, sizeof(unsigned char), 1, fw);
        }
    }
    fclose(fw);
    fclose(fp);
}
void Image::opposite(const char* ImageName)
{
    FILE* fp = NULL;
    fopen_s(&fp, ImageName, "rb");
    if (!fp)
    {
        cout << "cannot open file";
        exit(0);
    }
    fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);
    /*data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);*/
    /*data = new unsigned char* [bitmapinfoheader.biHeight];
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        data[i] = new unsigned char[bitmapinfoheader.biWidth];
    }*/
    unsigned char r, g, b;
    int width = bitmapinfoheader.biWidth;
    int height = bitmapinfoheader.biHeight;
    FILE* fw = NULL;
    fopen_s(&fw, "Copy.bmp", "wb");
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fw);
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fw);
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            fread(&r, sizeof(unsigned char), 1, fp);
            fread(&g, sizeof(unsigned char), 1, fp);
            fread(&b, sizeof(unsigned char), 1, fp);

            r = 255 - r;
            b = 255 - b;
            g = 255 - g;
            fwrite(&r, sizeof(unsigned char), 1, fw);
            fwrite(&g, sizeof(unsigned char), 1, fw);
            fwrite(&b, sizeof(unsigned char), 1, fw);
        }
    }
    fclose(fw);
    fclose(fp);
}
